GPU使用率をCloudWatchで取得してみよう
GPU使用率のモニタリング
- CloudWatchのカスタムメトリクスを利用してGPU使用率を取得します
- CPU使用率ではなくGPU使用率の取得方法です
GPUの使用率も可視化できる!
参考元
Amazon CloudWatch で GPU 使用率をモニタリング | Amazon Web Services ブログ
EC2の準備
GPU系のインスタンスを起動します。検証環境は下記の通りです。
項目 | 値 |
---|---|
OS | Amazon Linux2 |
AMI | Deep Learning AMI (Amazon Linux 2) Version 34.0 |
インスタンスタイプ | G4dn.xlarge |
EC2起動
Deep Learning
AMIを検索します。Deep Learning AMI (Amazon Linux 2) Version 34.0
からインスタンスを起動します。
IAMロール設定
カスタムメトリクスをCloudWatchに送信するためcloudwatch:PutMetricData
の許可が必要です。
AWS管理ポリシーのCloudWatchAgentServerPolicy
をアタッチしたIAMロールを作成し、EC2にロールを設定しました。
GPUモニタリングの準備
モニタリングスクリプトの編集と、実行環境の作成します。
GPUモニタリングスクリプトの確認
Deep Learning AMIは下記ディレクトリにモニタリングスクリプトが置いてあります。
/home/ec2-user/tools/GPUCloudWatchMonitor/gpumon.py
スクリプト(gpumon.py)の設定変更
スクリプトをお使いの環境に合わせて変更します。設定変更後のスクリプト全文を載せています。
検証環境では下記の変更をしました。
- EC2_REGION
us-east-1
からap-northeast-1
に変更
- my_NameSpace
DeepLearningTrain
からGPUMonitor
に変更
my_NameSpace
はCloudWatchのこの部分に表示される名前です。
# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License is located at # # http://www.apache.org/licenses/LICENSE-2.0 # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. import urllib.request as urllib2 import boto3 from pynvml import * from datetime import datetime from time import sleep ### CHOOSE REGION #### EC2_REGION = 'ap-northeast-1' ###CHOOSE NAMESPACE PARMETERS HERE### my_NameSpace = 'GPUMonitor' ### CHOOSE PUSH INTERVAL #### sleep_interval = 10 ### CHOOSE STORAGE RESOLUTION (BETWEEN 1-60) #### store_reso = 60 encoding = 'utf-8' #Instance information BASE_URL = 'http://169.254.169.254/latest/meta-data/' INSTANCE_ID = urllib2.urlopen(BASE_URL + 'instance-id').read().decode(encoding) IMAGE_ID = urllib2.urlopen(BASE_URL + 'ami-id').read().decode(encoding) INSTANCE_TYPE = urllib2.urlopen(BASE_URL + 'instance-type').read().decode(encoding) INSTANCE_AZ = urllib2.urlopen(BASE_URL + 'placement/availability-zone').read().decode(encoding) EC2_REGION = INSTANCE_AZ[:-1] TIMESTAMP = datetime.now().strftime('%Y-%m-%dT%H') TMP_FILE = '/tmp/GPU_TEMP' TMP_FILE_SAVED = TMP_FILE + TIMESTAMP # Create CloudWatch client cloudwatch = boto3.client('cloudwatch', region_name=EC2_REGION) # Flag to push to CloudWatch PUSH_TO_CW = True def getPowerDraw(handle): try: powDraw = nvmlDeviceGetPowerUsage(handle) / 1000.0 powDrawStr = '%.2f' % powDraw except NVMLError as err: powDrawStr = handleError(err) PUSH_TO_CW = False return powDrawStr def getTemp(handle): try: temp = str(nvmlDeviceGetTemperature(handle, NVML_TEMPERATURE_GPU)) except NVMLError as err: temp = handleError(err) PUSH_TO_CW = False return temp def getUtilization(handle): try: util = nvmlDeviceGetUtilizationRates(handle) gpu_util = str(util.gpu) mem_util = str(util.memory) except NVMLError as err: error = handleError(err) gpu_util = error mem_util = error PUSH_TO_CW = False return util, gpu_util, mem_util def logResults(i, util, gpu_util, mem_util, powDrawStr, temp): try: gpu_logs = open(TMP_FILE_SAVED, 'a+') writeString = str(i) + ',' + gpu_util + ',' + mem_util + ',' + powDrawStr + ',' + temp + '\n' gpu_logs.write(writeString) except: print("Error writing to file ", gpu_logs) finally: gpu_logs.close() if (PUSH_TO_CW): MY_DIMENSIONS=[ { 'Name': 'InstanceId', 'Value': INSTANCE_ID }, { 'Name': 'ImageId', 'Value': IMAGE_ID }, { 'Name': 'InstanceType', 'Value': INSTANCE_TYPE }, { 'Name': 'GPUNumber', 'Value': str(i) } ] cloudwatch.put_metric_data( MetricData=[ { 'MetricName': 'GPU Usage', 'Dimensions': MY_DIMENSIONS, 'Unit': 'Percent', 'StorageResolution': store_reso, 'Value': util.gpu }, { 'MetricName': 'Memory Usage', 'Dimensions': MY_DIMENSIONS, 'Unit': 'Percent', 'StorageResolution': store_reso, 'Value': util.memory }, { 'MetricName': 'Power Usage (Watts)', 'Dimensions': MY_DIMENSIONS, 'Unit': 'None', 'StorageResolution': store_reso, 'Value': float(powDrawStr) }, { 'MetricName': 'Temperature (C)', 'Dimensions': MY_DIMENSIONS, 'Unit': 'None', 'StorageResolution': store_reso, 'Value': int(temp) }, ], Namespace=my_NameSpace ) nvmlInit() deviceCount = nvmlDeviceGetCount() def main(): try: while True: PUSH_TO_CW = True # Find the metrics for each GPU on instance for i in range(deviceCount): handle = nvmlDeviceGetHandleByIndex(i) powDrawStr = getPowerDraw(handle) temp = getTemp(handle) util, gpu_util, mem_util = getUtilization(handle) logResults(i, util, gpu_util, mem_util, powDrawStr, temp) sleep(sleep_interval) finally: nvmlShutdown() if __name__=='__main__': main()
実行環境セットアップ
Deep Learningはセットアップ済みです。バージョンを確認します。
Python3の確認。
$ python3 -V Python 3.7.6
必要なライブラリを確認。
$ pip3 list |grep -e boto3 -e nvidia boto3 1.14.60 nvidia-ml-py 10.418.84
スクリプト実行
まずテスト実行してみます。数分放置してCloudWatchの画面します。
$ sudo python3 /home/ec2-user/tools/GPUCloudWatchMonitor/gpumon.py
GPUMonitorが増えています。
GPUMonitorをクリックするとメトリクスの項目が表示されます。
スクリプトを打ち切ります。
スクリプトのサービス化
ここからは参考記事のAmazon Web Services ブログの範囲外になります。GPUモニタリングのサービス化と、GPUへ負荷をかけてグラフを眺めて楽しみます。
$ sudo vi /etc/systemd/system/gpumon.service
ファイルの内容
[Unit] Description = gpumon daemon [Service] ExecStart =/bin/python3 /home/ec2-user/tools/GPUCloudWatchMonitor/gpumon.py Restart = always Type = simple [Install] WantedBy = multi-user.target
サービスの起動を確認します。
$ sudo systemctl daemon-reload $ sudo systemctl start gpumon $ sudo systemctl status gpumon $ sudo systemctl enable gpumon
GPUの負荷テスト
gpu-burn
を利用して手軽に負荷をかけてGPUをモニタリングできているか確認します。
wilicc/gpu-burn: Multi-GPU CUDA stress test
gpu-burnの準備と実行
$ git clone https://github.com/wilicc/gpu-burn $ cd gpu-burn $ make $ ./gpu_burn 600
10分間負荷をかけますので少し待ちましょう。コンソールには負荷テスト中の進捗が表示されます。GPUの温度が刻々と上がっていきます。
GPU 0: Tesla T4 (UUID: GPU-626c6bcb-eb2a-c1f8-e763-d6bd62691e7b) Initialized device 0 with 15109 MB of memory (14937 MB available, using 13443 MB of it), using FLOATS 10.7% proc'd: 10894 (4033 Gflop/s) errors: 0 temps: 54 C Summary at: 2020年 9月 16日 水曜日 08:25:12 UTC 20.8% proc'd: 24302 (3893 Gflop/s) errors: 0 temps: 65 C Summary at: 2020年 9月 16日 水曜日 08:26:13 UTC 30.8% proc'd: 37710 (3712 Gflop/s) errors: 0 temps: 71 C Summary at: 2020年 9月 16日 水曜日 08:27:13 UTC 41.5% proc'd: 51956 (3608 Gflop/s) errors: 0 temps: 75 C Summary at: 2020年 9月 16日 水曜日 08:28:17 UTC 51.7% proc'd: 63688 (3531 Gflop/s) errors: 0 temps: 77 C Summary at: 2020年 9月 16日 水曜日 08:29:18 UTC 53.3% proc'd: 66202 (3523 Gflop/s) errors: 0 temps: 77 C
グラフの確認
gpumon.pyはデフォルトの設定値では1分単位でメトリクスをPutしているため期間を変更すると細かく見えます。GPU使用率(オレンジのライン)は100%に達しています。
温度は80℃まで上がっていました。
おわりに
GPUインスタンスお使いの環境でしたらgpumon.pyを流用してメトリクスを取得するとなにかに役立つかもしれません。 当初は素のAmazon Linux2でGPUをモニタリングしようと試みたところ、GPUのドライバー周りのインストールに大変手間取り時間がかかり途中で打ち切りまして素直に設定済みのAMIを利用しました。なんでも一からセッティングを自分で頑張ればいいものではないなと思いました、時間は大切。